package com.wlyuan.open.interceptor;

import com.alibaba.cloud.commons.lang.StringUtils;
import com.wlyuan.open.annotation.OpenApi;
import com.wlyuan.open.api.IOpenApiCompanyService;
import com.wlyuan.open.api.IOpenClientService;
import com.wlyuan.open.constant.Constants;
import com.wlyuan.open.exception.CustomError;
import com.wlyuan.open.exception.CustomException;
import com.wlyuan.open.vo.OpenClientApiVO;
import com.wlyuan.open.vo.OpenClientDetailVO;
import com.wlyuan.web.claim.Claim;
import com.wlyuan.web.claim.ClaimUtils;
import lombok.extern.slf4j.Slf4j;
import lombok.var;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.CollectionUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Optional;

/**
 * @author lijing
 * @description
 */
@Component("customerAuthenticationInterceptor")
@Slf4j
public class CustomAuthenticationInterceptor implements HandlerInterceptor {
    @DubboReference(check = false)
    private IOpenClientService openClientService;
    @DubboReference(check = false)
    private IOpenApiCompanyService openApiCompanyService;

    /**
     * 处理器执行前调用
     *
     * @param request
     * @param response
     * @param object
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception {
        // 如果不是映射到方法直接通过
        if (object instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) object;
            //针对对外开放的api拦截
            var openApi = handlerMethod.getMethodAnnotation(OpenApi.class);
            if (openApi != null) {
                //获取登录信息
                Claim claim = ClaimUtils.get();
                logger.info("Claim info : {}", claim);
                if (claim == null) {
                    throw new CustomException(CustomError.NOT_LOGIN.getCode(), CustomError.NOT_LOGIN.getMessage());
                }
                String appId = claim.getAppId();
                Long tenantId = claim.getTenantId();
                //appId是否存在
                if (StringUtils.isBlank(appId) || Constants.APPID_NULL.equalsIgnoreCase(appId)) {
                    throw new CustomException(CustomError.APP_ID_NOT_FOUND.getCode(), CustomError.APP_ID_NOT_FOUND.getMessage());
                }
                var openClient = openClientService.getOpenClient(appId);
                logger.info("request url : {}", request.getRequestURL());
                //应用校验
                clientCheck(openClient, tenantId);
                //接口校验
                apiCheck(appId, openClient.getApiList(), request.getRequestURI(), request.getMethod());
            }
        }
        return true;
    }

    private void clientCheck(OpenClientDetailVO openClient, Long tenantId) {
        //开放平台应用不存在
        if (!Optional.ofNullable(openClient).isPresent()) {
            throw new CustomException(CustomError.APP_ID_NOT_FOUND.getCode(), CustomError.APP_ID_NOT_FOUND.getMessage());
        }
        //应用状态是否正常
        if (BooleanUtils.isFalse(openClient.getHasEnable())) {
            throw new CustomException(CustomError.APP_IS_DISABLE.getCode(), CustomError.APP_IS_DISABLE.getMessage());
        }
        //所属租户与当前租户是否一致
        if (tenantId != null && !tenantId.equals(openClient.getTenantId())) {
            throw new CustomException(CustomError.TENANT_ERROR.getCode(), CustomError.TENANT_ERROR.getMessage());
        }
        //判断用户账号状态是否正常
        if (BooleanUtils.isFalse(openClient.getAccountEnable())) {
            throw new CustomException(CustomError.WLY_ACCOUNT_ERROR.getCode(), CustomError.WLY_ACCOUNT_ERROR.getMessage());
        }
    }

    private void apiCheck(String appId, List<OpenClientApiVO> apiList, String requestUrl, String requestMethod) {
        //是否有申请接口
        if (CollectionUtils.isEmpty(apiList)) {
            throw new CustomException(CustomError.UNAUTHORIZED.getCode(), CustomError.UNAUTHORIZED.getMessage());
        }
        //所有接口url列表 （接口路径中存在/{orderId}动态参数，用url匹配判断url是否有权限访问）
        AntPathMatcher antPathMatcher = new AntPathMatcher();
        //接口是否授权，请求方式是否正确，申请接口状态是正常
        boolean passUrl = apiList.stream().anyMatch(uri -> {
            //根据url匹配，请求方式，接口启用状态。匹配平台的接口
            boolean result = antPathMatcher.match(uri.getUrl(), requestUrl) && requestMethod.equalsIgnoreCase(uri.getMethod()) && uri.getHasEnable();
            //接口匹配成功，则校验公司对接口的申请权限
            if (result) {
                //企业申请接口记录是否被禁用，不存在申请记录或者申请记录不是启用状态，则无权限访问
                var apply = openApiCompanyService.getOpenApiInfo(appId, uri.getApiId());
                return apply != null && apply.getHasEnable();
            }
            return false;
        });
        if (!passUrl) {
            throw new CustomException(CustomError.UNAUTHORIZED.getCode(), CustomError.UNAUTHORIZED.getMessage());
        }
    }

    /**
     * 在业务处理器处理请求完成之后，生成视图之前执行
     *
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    /**
     * 整个过程结束后调用
     *
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}
